﻿using System.Collections.Generic;
using System.Linq;

namespace BioinformaticAlgoritms2014
{
    public class DNASequence : SequenceBase
    {
        public DNASequence(string sequence):base(sequence)
        {
        }

        public RNASequence GetRnaSequence()
        {
            return RNASequence.LoadFromDnaString(Sequence);
        }
     

        public int[] GetSkew()
        {
            int c = 0, g = 0;
            var result = new int[Sequence.Length];
            for (int i = 0; i < Sequence.Length; i++)
            {
                switch (Sequence[i])
                {
                    case 'C':
                        c++;
                        break;
                    case 'G':
                        g++;
                        break;
                }
                result[i] = (g - c);
            }
            return new int[] { 0 }.Concat(result).ToArray();
        }

        public int GetHammingDistance(string seq)
        {
            return Extensions.HammingDistance(Sequence, seq);
        }

        public int GetPatternCount(string pattern, int mismatches)
        {
            return GetPatternMatchesIndexes(pattern, mismatches).Length;
        }

        public string[] GetFrequentWords(int k)
        {
            return GetFrequentWords(k, 0);
        }

       public string[] GetFrequentWords(int k, int mismatches)
       {
           return GetFrequentWords(k, mismatches, false);
       }
       public string[] GetFrequentWords(int k, int mismatches, bool reverseComplement)
       {
           var matching = GetWordsMatchng(Sequence, k);
           int maxCount = 0;
           if (mismatches > 0)
           {
               
               var allWords = Extensions.GetAllDnaWords(k).Distinct();
               //TODO: работает долго, придумать как переделать
               var matches =
                   allWords.Select(
                       a =>
                       {
                           string reverseCompl =reverseComplement? a.GetComplement().Reverse():"";
                           return new
                           {
                               Pattern = a,
                               Count =
                                   matching.Keys.Where(
                                       b =>
                                           Extensions.HammingDistance(a, b) <= mismatches ||
                                           reverseComplement &&
                                           Extensions.HammingDistance(reverseCompl, b) <= mismatches)
                                       .Sum(b => matching[b].Count)
                           };
                       }).ToList();
               maxCount = matches.Max(a => a.Count);
               return matches.Where(a => a.Count == maxCount).Select(a => a.Pattern).Distinct().ToArray();
           }
           maxCount = matching.Values.Select(a => a.Count).Max();
           return matching.Where(a => a.Value.Count == maxCount).Select(a => a.Key).ToArray();
       }


        public string GetReverseComplement()
        {
            return Sequence.GetComplement().Reverse();
        }

        public int[] GetPatternMatchesIndexes(string pattern, int mismatches)
        {
            pattern = pattern.ToUpper();
            var matches = GetWordsMatchng(Sequence, pattern.Length);
            return matches.Keys.Where(a => Extensions.HammingDistance(a, pattern) <= mismatches)
                .SelectMany(a => matches[a].Indexes).Distinct().ToArray();

        }
        /// <summary>
        /// Peptide Encoding Problem: Find substrings of a genome encoding a given amino acid sequence.
        ///Input: An amino acid string Peptide, and the array GeneticCode.
        ///Output: All substrings of Text encoding Peptide (if any such substrings exist)
        /// </summary>
        public string[] GetPeptideSubstring(string peptid)
        {
            peptid = peptid.ToUpper();

            return GetAllSubstrings(peptid.Length*3).Where(a =>
                    {
                        var seq = new DNASequence(a);
                        return seq.GetRnaSequence().TranslateToAminoAcidSeq() == peptid ||
                               new DNASequence(seq.GetReverseComplement()).GetRnaSequence().TranslateToAminoAcidSeq() ==
                               peptid;
                    }).ToArray();
        }




        /// <summary>
        /// Возвращает к-меры в окне длинной l, которые встречаются не меньше, чем t раз.  (L, t)-clumps
        /// </summary>
        /// <param name="l">длинна окна</param>
        /// <param name="k">длинна к-мера</param>
        /// <param name="t">минимальное количество</param>
        /// <returns></returns>
        public string[] Find(int k, int l, int t)
        {
            return GetWordsMatchng(Sequence, k).Where(a => a.Value.MaxMatchesInWindow(l) >= t).Select(a => a.Key).ToArray();
        }
        /// <summary>
        /// Словарь совпадений: ключ - паттерн, значение - количество совпадений
        /// </summary>
        /// <param name="seq">Последовательность</param>
        /// <param name="k">Длинна к-мера</param>
        /// <returns></returns>
        private static Dictionary<string, Match> GetWordsMatchng(string seq, int k)
        {
            var result = new Dictionary<string, Match>();
            for (int i = 0; i <= seq.Length - k; i++)
            {

                var pattern = seq.Substring(i, k);
                if (result.ContainsKey(pattern))
                {
                    result[pattern].Increase(i);
                }
                else
                {
                    result[pattern] = new Match(pattern, i);
                }
            }
            return result;
        }

        private IEnumerable<string> GetAllSubstrings(int length)
        {
            for (int i = 0; i < Sequence.Length - length; i++)
            {
                yield return Sequence.Substring(i, length);
            }
        } 

 
    }
}
